<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="Cache-Control" content="no-siteapp">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=1, minimum-scale=1, maximum-scale=1">
<meta name="referrer" content="never">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.png?v=198964">
<link rel="apple-itouch-icon" href="/favicon.png?v=198964">
<link href="/bundle/index.min.css" rel="stylesheet">
<link href="https://fonts.loli.net/css?family=Merriweather:300,700,700italic,300italic|Open+Sans:700,400" rel="stylesheet">
<link href="https://cdn.staticfile.org/prism/1.16.0/themes/prism.min.css" rel="stylesheet">
<link href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script>
    function ensureDate(e){return"object"!=typeof e&&(e=new Date(e)),e}function dateFormat(e,t){void 0===t&&(t=e,e=ensureDate());let n={M:(e=ensureDate(e)).getMonth()+1,d:e.getDate(),h:e.getHours(),m:e.getMinutes(),s:e.getSeconds(),q:Math.floor((e.getMonth()+3)/3),S:e.getMilliseconds()},r=new RegExp("([yMdhmsqS])+","g");return t=t.replace(r,function(t,r){let u=n[r];if(void 0!==u)return t.length>1&&(u=(u="0"+u).substring(u.length-2)),u;if("y"===r){return(e.getFullYear()+"").substring(4-t.length)}return t})}
</script>
    <meta name="keywords" content="React 组件开发入门,React,">
    <meta name="description" content="React 组件开发入门,React,">
    <meta name="author" content="江矿先森.">
    <title>React 组件开发入门</title>
    <link href="/bundle/iconfont.css" rel="stylesheet">
    <link href="/bundle/reward.css" rel="stylesheet">
    <script src='/bundle/av.min.js'></script>
    <script src='/bundle/valine.min.js'></script>
</head>

<body>
    <article class="container">
        <header class="header-wrap asset">
    <nav class="main-nav">
        <ul class="menu vertical naive">
            <li class="menu-item"><a href="/">Home</a></li>
            <li class="menu-item"><a href="/archive.html">Archive</a></li>
            <li class="menu-item"><a href="/tag.html">Tag</a></li>
            
            <li class="menu-item"><a href="/atom.xml">RSS</a></li>
        </ul>
    </nav>
    <div class="bgs" style="background-image: url(https://pic6.58cdn.com.cn/nowater/webim/big/n_v212fe49b1d6d5492cb1def84eeff4b142.jpg);"></div>
    <div class="vertical">
        <div class="header-wrap-content inner">
            <h3 class="title">Stay before every beautiful thoughts.</h3>
            <h3 class="subtitle">Just be nice, always think twice!</h3>
        </div>
    </div>
</header>
        <article class="main article">
            <h1 class="title">React 组件开发入门</h1>
            <section class="info">
                <span class="avatar" style="background-image: url(https://pic1.58cdn.com.cn/nowater/webim/big/n_v2a9773b605e754c8493cf53ca8f5c0d7e.jpg);"></span> <a class="name" href="javascript:;">江矿先森.</a> 
                <span class="date"><script>document.write(dateFormat( 1516271034 *1000, 'yyyy-MM-dd'))</script></span> 
                <span class="tags"><a class="tages" href="/tag/React/index.html">React</a></span>
            </section>
            <article class="content"><p>熟悉 React 的思想后，我们先来尝试开发一个单纯的小组件，可以对比一下是不是比以前的开发模式更加舒适了，这里我主要以一个 Loading 组件来举栗子，实现了几个基本的功能：</p>

<ul>
<li>一种类型的 loading（菊花转）</li>
<li>能够设置 loading 的三个属性：width height color</li>
<li>能够控制 loading 的显示和隐藏</li>
</ul>

<p>其实对于一个简单需求来说，这三个属性已经很实用了。但是去网上看一些外国大神写的组件，有一些不明白的地方，所以自己就慢慢搞，do it！</p>

<h4>设计</h4>

<p>我想这样用 loadding 组件：</p>

<pre><code class="language-js">var loadingClasses = cx({
    loadding: true,
    active: this.state.isActiveLoading
    });

    &lt;Loading width={30} height={30} color={#000} className={loadingClasses} /&gt;
</code></pre>

<p>所以我定义这个组件的基本结构如下：</p>

<pre><code class="language-js">var Loading = React.createClass({
    // 控制组件属性的类型
    propTypes: {},
    // 控制组件属性的默认值
    getDefaultProps: function () {},
    // 组装基本的内联样式
    getComponentStyle: function () {},
    // 渲染基本的组件，拆分 render 方法的粒度
    renderBaseComp: function () {},
    // 最终的渲染方法
    render: function () {}
});
</code></pre>

<p>这个组件中，我使用的 内联样式 来控制组件的内部基本样式的稳定。其实有时候我们会觉得内联样式不好，但是我个人觉得每一种设置 CSS 形式的方法，用在合适的场景中就是正确的。</p>

<p>每部分的具体实现如下，代码中有一些讲解（这里我不会介绍具体 loading 效果是怎么出来的，看代码应该就会明白，主要介绍一个 react 制作简单组件的思路和写法）对于扩展性来说，</p>

<pre><code>你还可以加入 className 和 type 这些修饰性的属性，但是我更倾向于迭代式的组件开发，
小组件就要具有良好的封闭性，使用接口简单。大组件才考虑更好的鲁棒性和可扩展性，
这样开发一个组件的性价比才高。需要注意对 getDefaultProps的理解，
只有当使用接口的人代码中根本没有写那个属性的时候，才会使用定义的默认值。
</code></pre>

<h4>实现</h4>

<pre><code class="language-js">var Loading = React.createClass({
    propTypes: {
        width: React.PropTypes.oneOfType([
            React.PropTypes.number,
            React.PropTypes.string
        ]),
        height: React.PropTypes.oneOfType([
            React.PropTypes.number,
            React.PropTypes.string
        ]),
        color: React.PropTypes.string,
        active: React.PropTypes.bool
    },
    getDefaultProps: function() {
        return {
            color: &quot;#00be9c&quot;,
            height: 30,
            width: 30,
            active: false
        };
    },

    getComponentStyle: function() {
        var width = this.props.width,
            height = this.props.height,
            color = this.props.color;
        /* 中间圆心 */
        var cWidth = 0.4 * width,
            cHeight = 0.4 * height,
            cMarginLeft = -0.5 * cWidth,
            cMarginTop = -0.5 * cHeight;

        /* 基本样式 */
        return {
            loadingStyle: { // loadding 容器
                width: width,
                height: height
            },
            lineStyle: { // loadding 元件样式
                background: color
            },
            centerStyle: { // loadding 圆心样式
                width: cWidth,
                height: cHeight,
                marginLeft: cMarginLeft,
                marginTop: cMarginTop
            }
        };
    },

    renderBaseComp: function(compStyle) {
        /* 生成动画元件 */
        var n = 4; // 元件个数，todo: 定制个数
        var lines = []; // 元件元素集合
        for (var i = 0; i &lt; n; i++) {
            lines.push(
                &lt;div className=&quot;line&quot;&gt;
                    &lt;span className=&quot;top&quot; style={ compStyle.lineStyle }&gt;&lt;/span&gt;
                    &lt;span className=&quot;bottom&quot; style={ compStyle.lineStyle }&gt;&lt;/span&gt;
                &lt;/div&gt;
            );
        }
        return lines;
    },

    render: function() {
        /* 生成组件自己的样式 */
        var compStyle = this.getComponentStyle();
        /* 模拟渲染基本动画元件 */
        var lines = this.renderBaseComp(compStyle);

        // loadding 的class，控制交互
        var loadingClasses = cx({
            loading: true,
            active: this.props.active
        });

        return (
            &lt;div className={ loadingClasses } style={ compStyle.loadingStyle }&gt;
                {lines}
                &lt;div className=&quot;loading-center&quot; style={ compStyle.centerStyle }&gt;&lt;/div&gt;
            &lt;/div&gt;

        );
    }

});
</code></pre>

<h5>最后，下面是基本的 SASS（不考虑不支持的情况，不支持都不用开发，直接用图，性价比更高）</h5>

<pre><code class="language-css">@include keyframes(load) {
    0% {
        opacity: 0;
    }
    25% {
        opacity: .25;
    }
    50% {
        opacity: .5;
    }
    75% {
        opacity: .75;
    }
    100% {
        opacity: 1;
    }
}

.loading {
    display: none;
    position: absolute;
    &amp;.active {
        display: block;
    }
    .loading-center {
        position: absolute;
        left: 0;
        top: 50%;
        background: #fff;
        border-radius: 50%;
    }
    .line {
        position: absolute;
        top: 0;
        left: 0;
        height: 100%;
        .top {
            content: &quot;&quot;;
            display: block;
            width: 1px;
            font-size: 0;
            height: 50%;
        }
        .bottom {
            @extend .top;
        }
        @for $i from 1 through 4 {
            &amp;:nth-child(#{$i}) {
                transform:rotate(45deg * ($i - 1));
                .top {
                    @include animation(load, 0.8s, linear, 0s, infinite);
                }
                .bottom {
                    @include animation(load, 0.8s, linear, 0.4s + $i/10, infinite);
                }
            }
        }
    }
}
</code></pre>

<p>里面用到的一个 animation 混淆方法：</p>

<pre><code class="language-css">@mixin keyframes($name) {
    @-webkit-keyframes #{$name} {
        @content;
    }
    @-moz-keyframes #{$name} {
        @content;
    }
    @-ms-keyframes #{$name} {
        @content;
    }
    @keyframes #{$name} {
        @content;
    }
}

@mixin animation ($name, $duration, $func, $delay, $count, $direction: normal) {
    -webkit-animation: $name $duration $func $delay $count $direction;
    -moz-animation: $name $duration $func $delay $count $direction;
    -o-animation: $name $duration $func $delay $count $direction;
    animation: $name $duration $func $delay $count $direction;
}
</code></pre>
</article>
            <section class="author">
                
                <div class="avatar" style="background-image: url(https://pic1.58cdn.com.cn/nowater/webim/big/n_v2a9773b605e754c8493cf53ca8f5c0d7e.jpg);"></div>
                <a class="name" href="javascript:;">江矿先森.</a>
                <div class="intro">前(台)端(菜), 喜欢瞎折腾新技术. 乜野都识少少, 先可以扮代表.</div>
            </section>
            <section class="social">
                <a href="https://github.com/joname1" target="_blank">
                    <i class="iconfont i-github"></i>
                </a>
                <a href="javascript:alert('你电脑中了不知名的病毒, 并抛出了警告 atob(“d3hJZDogam9uYW1lLmxpYW5ndGFu”)')" target="_blank">
                    <i class="iconfont i-wechat"></i>
                </a>
                <a href="https://www.zhihu.com/people/joname-liangtan" target="_blank">
                    <i class="iconfont i-zhihu"></i>
                </a>
                <a href="javascript:alert('对方不想跟你讲话, 并向你扔来一段乱码 atob(“am9uYW1lLmxpYW5ndGFuQGdtYWlsLmNvbQ”)')" target="_blank">
                    <i class="iconfont i-email"></i>
                </a>
            </section>

            <div class="reward">
                <div class="reward-button">赏
                    <span class="reward-code">
                        <span class="alipay-code">
                            <img class="alipay-img wdp-appear" src="https://pic1.58cdn.com.cn/nowater/webim/big/n_v24d5eae197a764eb194b61e366ff263ba.jpg"><b>支付宝扫码</b>
                        </span>
                    <span class="wechat-code">
                            <img class="wechat-img wdp-appear" src="https://pic1.58cdn.com.cn/nowater/webim/big/n_v20ec7ae162f6a4219b8d0b429a2dbc9b3.jpg"><b>微信扫码</b>
                        </span>
                    </span>
                </div>
                
                <p class="reward-notice">如果文章对你有帮助, 扫上方二维码请我喝杯奶茶吧 :p</p>
            </div>

            <div id="comment"></div>
            
        </article>
    </article>
    <footer class="footer clearfix">
    <span class="copyright">
        <script>
            document.write(new Date().getFullYear());
        </script> <i class="fa fa-copyright"></i> Made with <i class="fa fa-heart"></i> using &ltjoname /&gt
        <span id="runtime_span"></span>
    </span>
</footer>
    <script src="/bundle/index.min.js"></script>
    <script src="https://cdn.staticfile.org/prism/1.16.0/prism.min.js"></script>
    <script>
        new Valine({el: '#comment',appId: 'PieJ3iHvVTJ9C5yBudK6sxaT-MdYXbMMI',appKey: 'Yt25unM4vc9wzBvC2lL20Frc',placeholder: 'ヾﾉ≧∀≦)o来啊, 快活啊, 反正有大把时光!!',path: window.location.pathname,avatar: 'retro',pageSize: 10,guest_info: ['nick', 'mail'],lang: 'en'});
    </script>
</body>

</html>